package com.lei.sandbox;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.lei.sandbox.model.ExecuteCodeRequest;
import com.lei.sandbox.model.ExecuteCodeResponse;
import com.lei.sandbox.model.ExecuteMessage;
import com.lei.sandbox.model.JudgeInfo;
import com.lei.sandbox.utils.ProcessUtils;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 代码沙箱通用模板（支持多种语言）
 * @author lei
 */
@Slf4j
public abstract class CodeSandboxTemplate implements CodeSandbox{

    private static final String GLOBAL_CODE_DIR_NAME = "tmpCode";

    private static final String GLOBAL_JAVA_CLASS_NAME = "Main.java";

    private static final long TIME_OUT = 5000L;
    
    // 编译结果缓存，限制最大缓存数量和过期时间
    private static final CompileCache COMPILE_CACHE = new CompileCache(200, 60 * 60 * 1000); // 最大200个条目，过期时间1小时

    /**
     * 1、把用户代码保存为文件
     * @return
     */
    public File saveCodeToFile(String code){
        //        1.  把用户的代码保存为文件
        String userDir = System.getProperty("user.dir");
        String globalCodePathName = userDir + File.separator + GLOBAL_CODE_DIR_NAME;
        // 判断全局代码目录是否存在，没有则新建
        if (!FileUtil.exist(globalCodePathName)) {
            FileUtil.mkdir(globalCodePathName);
        }
        // 把用户的代码隔离存放
        String userCodeParentPath = globalCodePathName + File.separator + UUID.randomUUID();
        String userCodePath = userCodeParentPath + File.separator + GLOBAL_JAVA_CLASS_NAME;
        File userCodeFile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
        return  userCodeFile;
    }

    /**
     * 编译代码文件
     * @param userCodeFile
     * @return
     */
    public ExecuteMessage compileFile(File userCodeFile){
        String codePath = userCodeFile.getAbsolutePath();
        // 检查缓存中是否已存在编译结果
        if (COMPILE_CACHE.contains(codePath)) {
            System.out.println("使用缓存的编译结果");
            ExecuteMessage cachedMessage = new ExecuteMessage();
            cachedMessage.setExitValue(0);
            cachedMessage.setMessage("Cached compilation result");
            return cachedMessage;
        }
        
        // 2.  编译代码，得到 class 文件
        // 优化编译命令，添加更多参数以提高编译效率
        ProcessBuilder compileProcessBuilder = new ProcessBuilder(
            "javac", 
            "-encoding", "utf-8",
            "-J-Xmx256m",  // 限制编译过程的内存使用
            "-XDignore.symbol.file",  // 忽略符号文件以加快编译
            userCodeFile.getAbsolutePath()
        );
        try {
            Process compileProcess = compileProcessBuilder.start();
            ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(compileProcess, "编译");
            if(executeMessage.getExitValue()!=0){
                // 输出编译错误详情
                System.err.println("compileFile函数编译错误详情: " + executeMessage.getErrorMessage());
                throw new RuntimeException("CompileError");
            } else {
                // 编译成功，将结果存入缓存
                COMPILE_CACHE.put(codePath, true);
            }
            return executeMessage;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 执行文件，获得执行文件结果（支持标准输入输出交互）
     * @param userCodeFile
     * @param inputList
     * @return
     */
    public List<ExecuteMessage> runFile(File userCodeFile, List<String> inputList) {
        String userCodeParentPath = userCodeFile.getParentFile().getAbsolutePath();

        // 检查Main.class文件是否存在
        File classFile = new File(userCodeParentPath + File.separator + "Main.class");
        if (!classFile.exists()) {
            throw new RuntimeException("编译后的Main.class文件不存在，请检查编译过程");
        }

        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        // 使用线程池并行执行多个测试用例
        ExecutorService executor = Executors.newFixedThreadPool(Math.min(inputList.size(), 5));
        List<Future<ExecuteMessage>> futures = new ArrayList<>();

        for (String inputArgs : inputList) {
            Future<ExecuteMessage> future = executor.submit(() -> {
                try {
                    // 构造Java执行命令
                    List<String> command = new ArrayList<>();
                    command.add("java");
                    command.add("-Xmx256m");
                    command.add("-Dfile.encoding=UTF-8");
                    command.add("-cp");
                    command.add(userCodeParentPath);
                    command.add("Main");

                    // 使用ProcessBuilder执行命令
                    ProcessBuilder processBuilder = new ProcessBuilder(command);
                    Process runProcess = processBuilder.start();

                    // 创建超时控制线程
                    final boolean[] finished = {false};
                    Thread timeoutThread = new Thread(() -> {
                        try {
                            Thread.sleep(TIME_OUT);
                            if (!finished[0] && runProcess.isAlive()) {
                                System.out.println("超时了，中断 ");
                                runProcess.destroy();
                                if (runProcess.isAlive()) {
                                    runProcess.destroyForcibly();
                                }
                            }
                        } catch (InterruptedException e) {
                            // 线程被中断，正常退出
                        }
                    });
                    timeoutThread.start();

                    // 向进程写入输入数据
                    try (OutputStreamWriter writer = new OutputStreamWriter(runProcess.getOutputStream())) {
                        if (inputArgs != null) {
                            writer.write(inputArgs);
                            writer.flush();
                        }
                        // 关闭输入流，表示输入结束
                        runProcess.getOutputStream().close();
                    }

                    // 获取执行结果
                    ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(runProcess, "运行");
                    finished[0] = true; // 标记已完成

                    // 中断超时线程
                    timeoutThread.interrupt();

                    return executeMessage;
                } catch (Exception e) {
                    throw new RuntimeException("程序执行异常", e);
                }
            });
            futures.add(future);
        }

        // 收集所有执行结果
        for (Future<ExecuteMessage> future : futures) {
            try {
                ExecuteMessage message = future.get(TIME_OUT + 1000, TimeUnit.MILLISECONDS);
                executeMessageList.add(message);
            } catch (Exception e) {
                throw new RuntimeException("执行超时或出现异常", e);
            }
        }

        // 关闭线程池
        executor.shutdown();
        return executeMessageList;
    }





    /**
     * 获得输出相应结果
     * @param executeMessageList
     * @return
     */
    public ExecuteCodeResponse getOutputResponse(List<ExecuteMessage> executeMessageList){
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        List<String> outputList = new ArrayList<>();

        // 取用时最大值，便于判断是否超时
        long maxTime = 0;
        long maxMemory = 0;

        for(ExecuteMessage executeMessage : executeMessageList){
            String errorMessage = executeMessage.getErrorMessage();
            if(StrUtil.isNotBlank(errorMessage)){
                executeCodeResponse.setMessage(errorMessage);
                //执行中存在错误
                executeCodeResponse.setStatus(3);
                break;
            }
            outputList.add(executeMessage.getMessage());

            //取最大值
            Long time = executeMessage.getTime();
            if (time != null) {
                maxTime = Math.max(maxTime, time);
            }
            Long memory = executeMessage.getMemory();
            if (memory != null && memory > 0){
                maxMemory = Math.max(maxMemory , memory);
            }
        }
        //每条都正常输出
        if(outputList.size() == executeMessageList.size()){
            executeCodeResponse.setStatus(2);
        }
        executeCodeResponse.setOutputList(outputList);

        //正常运行
        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setTime(maxTime);
        judgeInfo.setMemory(maxMemory);
        executeCodeResponse.setJudgeInfo(judgeInfo);
        return executeCodeResponse;
    }

    /**
     * 文件清理
     * @param userCodeFile
     * @return
     */
    public boolean deleteFile(File userCodeFile){
        if (userCodeFile.getParentFile() != null) {
            String userCodeParentPath = userCodeFile.getParentFile().getAbsolutePath();
            boolean del = FileUtil.del(userCodeParentPath);
            System.out.println("删除" + (del ? "成功" : "失败"));
            // 从缓存中移除编译结果
            COMPILE_CACHE.remove(userCodeFile.getAbsolutePath());
            return del;
        }
        return true;
    }

    /**
     * 获取错误响应
     */
    protected ExecuteCodeResponse getErrorResponse(Throwable e) {
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        executeCodeResponse.setOutputList(new ArrayList<>());

        String message = e.getMessage();
        if (message != null && message.contains("CompileError")) {
            executeCodeResponse.setMessage("CompileError");
        } else {
            executeCodeResponse.setMessage(message);
        }

        // 表示代码沙箱错误
        executeCodeResponse.setStatus(3);
        JudgeInfo judgeInfo = new JudgeInfo();

        if (message != null && message.contains("CompileError")) {
            judgeInfo.setMessage("CompileError");
        } else {
            judgeInfo.setMessage(message);
        }

        executeCodeResponse.setJudgeInfo(judgeInfo);
        return executeCodeResponse;
    }


    @Override
    public ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest) {
        List<String> inputList = executeCodeRequest.getInputList();
        String code = executeCodeRequest.getCode();
        String language = executeCodeRequest.getLanguage();

        File userCodeFile = null;
        try {
            //1、保存代码文件
            userCodeFile = saveCodeToFile(code);

            //2、编译代码文件得到Class文件
            ExecuteMessage compileFileExecuteMessage = compileFile(userCodeFile);

            //3. 执行代码，得到输出结果
            List<ExecuteMessage> executeMessageList = runFile(userCodeFile, inputList);

            //4. 收集整理输出结果
            ExecuteCodeResponse outputResponse = getOutputResponse(executeMessageList);
            System.out.println("outputResponse : " + outputResponse);
            
            return outputResponse;
        } catch (Exception e) {
            // 返回编译错误或运行时错误的响应
            return getErrorResponse(e);
        } finally {
            //5. 文件清理，释放空间
            if (userCodeFile != null) {
                boolean b = deleteFile(userCodeFile);
                if (!b) {
                    log.error("deleteFile error,userCodeFilePath = {}", userCodeFile.getAbsolutePath());
                }
            }
        }
    }

    /**
     * 编译结果缓存实现
     */
    static class CompileCache {
        private final int maxSize;
        private final long expireTime;
        private final ConcurrentHashMap<String, CacheEntry<Boolean>> cache;
        private final AtomicInteger cacheSize = new AtomicInteger(0);
        
        public CompileCache(int maxSize, long expireTime) {
            this.maxSize = maxSize;
            this.expireTime = expireTime;
            this.cache = new ConcurrentHashMap<>();
        }
        
        public boolean contains(String key) {
            CacheEntry<Boolean> entry = cache.get(key);
            if (entry == null) {
                return false;
            }
            
            if (System.currentTimeMillis() - entry.timestamp > expireTime) {
                // 过期了，移除
                cache.remove(key);
                cacheSize.decrementAndGet();
                return false;
            }
            
            return true;
        }
        
        public void put(String key, Boolean value) {
            // 检查是否达到最大容量
            if (cacheSize.get() >= maxSize) {
                clearExpiredEntries();
                
                // 如果仍然满了，清除一些条目
                if (cacheSize.get() >= maxSize) {
                    removeOldestEntries(maxSize / 10);
                }
            }
            
            cache.put(key, new CacheEntry<>(value, System.currentTimeMillis()));
            cacheSize.incrementAndGet();
        }
        
        public void remove(String key) {
            if (cache.remove(key) != null) {
                cacheSize.decrementAndGet();
            }
        }
        
        private void clearExpiredEntries() {
            long currentTime = System.currentTimeMillis();
            cache.entrySet().removeIf(entry -> {
                boolean expired = currentTime - entry.getValue().timestamp > expireTime;
                if (expired) {
                    cacheSize.decrementAndGet();
                }
                return expired;
            });
        }
        
        private void removeOldestEntries(int count) {
            // 移除最旧的条目
            cache.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e1.getValue().timestamp, e2.getValue().timestamp))
                .limit(count)
                .forEach(entry -> {
                    cache.remove(entry.getKey());
                    cacheSize.decrementAndGet();
                });
        }
        
        private static class CacheEntry<V> {
            V value;
            long timestamp;
            
            CacheEntry(V value, long timestamp) {
                this.value = value;
                this.timestamp = timestamp;
            }
        }
    }
}